{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c3e2458f-d038-4845-93a0-d4ad830f9f90",
   "metadata": {},
   "source": [
    "# LangChain 核心模块学习：Chains\n",
    "\n",
    "对于简单的大模型应用，单独使用语言模型（LLMs）是可以的。\n",
    "\n",
    "**但更复杂的大模型应用需要将 `LLMs` 和 `Chat Models` 链接在一起 - 要么彼此链接，要么与其他组件链接。**\n",
    "\n",
    "LangChain 为这种“链式”应用程序提供了 `Chain` 接口。\n",
    "\n",
    "LangChain 以通用方式定义了 `Chain`，它是对组件进行调用序列的集合，其中可以包含其他链。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "be885177-e986-4c77-8fc4-bb0b70bac5d8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: langchain in c:\\users\\lenovo\\appdata\\roaming\\python\\python310\\site-packages (0.2.7)\n",
      "Requirement already satisfied: PyYAML>=5.3 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from langchain) (6.0.1)\n",
      "Requirement already satisfied: SQLAlchemy<3,>=1.4 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from langchain) (2.0.31)\n",
      "Requirement already satisfied: aiohttp<4.0.0,>=3.8.3 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from langchain) (3.9.5)\n",
      "Requirement already satisfied: async-timeout<5.0.0,>=4.0.0 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from langchain) (4.0.3)\n",
      "Requirement already satisfied: langchain-core<0.3.0,>=0.2.12 in c:\\users\\lenovo\\appdata\\roaming\\python\\python310\\site-packages (from langchain) (0.2.13)\n",
      "Requirement already satisfied: langchain-text-splitters<0.3.0,>=0.2.0 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from langchain) (0.2.0)\n",
      "Requirement already satisfied: langsmith<0.2.0,>=0.1.17 in c:\\users\\lenovo\\appdata\\roaming\\python\\python310\\site-packages (from langchain) (0.1.85)\n",
      "Requirement already satisfied: numpy<2,>=1 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from langchain) (1.26.4)\n",
      "Requirement already satisfied: pydantic<3,>=1 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from langchain) (2.5.3)\n",
      "Requirement already satisfied: requests<3,>=2 in c:\\users\\lenovo\\appdata\\roaming\\python\\python310\\site-packages (from langchain) (2.32.3)\n",
      "Requirement already satisfied: tenacity!=8.4.0,<9.0.0,>=8.1.0 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from langchain) (8.4.2)\n",
      "Requirement already satisfied: aiosignal>=1.1.2 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.3.1)\n",
      "Requirement already satisfied: attrs>=17.3.0 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (23.2.0)\n",
      "Requirement already satisfied: frozenlist>=1.1.1 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.4.1)\n",
      "Requirement already satisfied: multidict<7.0,>=4.5 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (6.0.5)\n",
      "Requirement already satisfied: yarl<2.0,>=1.0 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.9.4)\n",
      "Requirement already satisfied: jsonpatch<2.0,>=1.33 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from langchain-core<0.3.0,>=0.2.12->langchain) (1.33)\n",
      "Requirement already satisfied: packaging<25,>=23.2 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from langchain-core<0.3.0,>=0.2.12->langchain) (23.2)\n",
      "Requirement already satisfied: orjson<4.0.0,>=3.9.14 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from langsmith<0.2.0,>=0.1.17->langchain) (3.10.5)\n",
      "Requirement already satisfied: annotated-types>=0.4.0 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from pydantic<3,>=1->langchain) (0.6.0)\n",
      "Requirement already satisfied: pydantic-core==2.14.6 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from pydantic<3,>=1->langchain) (2.14.6)\n",
      "Requirement already satisfied: typing-extensions>=4.6.1 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from pydantic<3,>=1->langchain) (4.12.2)\n",
      "Requirement already satisfied: charset-normalizer<4,>=2 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from requests<3,>=2->langchain) (3.3.2)\n",
      "Requirement already satisfied: idna<4,>=2.5 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from requests<3,>=2->langchain) (3.6)\n",
      "Requirement already satisfied: urllib3<3,>=1.21.1 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from requests<3,>=2->langchain) (2.2.2)\n",
      "Requirement already satisfied: certifi>=2017.4.17 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from requests<3,>=2->langchain) (2024.2.2)\n",
      "Requirement already satisfied: greenlet!=0.4.17 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from SQLAlchemy<3,>=1.4->langchain) (3.0.3)\n",
      "Requirement already satisfied: jsonpointer>=1.9 in c:\\programdata\\anaconda3\\envs\\langchain\\lib\\site-packages (from jsonpatch<2.0,>=1.33->langchain-core<0.3.0,>=0.2.12->langchain) (3.0.0)\n"
     ]
    }
   ],
   "source": [
    "! pip install -U langchain"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f9cf0d43-107b-47ae-9e2c-2edaec38c800",
   "metadata": {},
   "source": [
    "## Chain Class 基类\n",
    "\n",
    "类继承关系：\n",
    "\n",
    "```\n",
    "Chain --> <name>Chain  # Examples: LLMChain, MapReduceChain, RouterChain\n",
    "```\n",
    "\n",
    "**代码实现：https://github.com/langchain-ai/langchain/blob/master/libs/langchain/langchain/chains/base.py**\n",
    "\n",
    "```python\n",
    "# 定义一个名为Chain的基础类\n",
    "class Chain(Serializable, Runnable[Dict[str, Any], Dict[str, Any]], ABC):\n",
    "    \"\"\"为创建结构化的组件调用序列的抽象基类。\n",
    "    \n",
    "    链应该用来编码对组件的一系列调用，如模型、文档检索器、其他链等，并为此序列提供一个简单的接口。\n",
    "    \n",
    "    Chain接口使创建应用程序变得容易，这些应用程序是：\n",
    "    - 有状态的：给任何Chain添加Memory可以使它具有状态，\n",
    "    - 可观察的：向Chain传递Callbacks来执行额外的功能，如记录，这在主要的组件调用序列之外，\n",
    "    - 可组合的：Chain API足够灵活，可以轻松地将Chains与其他组件结合起来，包括其他Chains。\n",
    "    \n",
    "    链公开的主要方法是：\n",
    "    - `__call__`：链是可以调用的。`__call__`方法是执行Chain的主要方式。它将输入作为一个字典接收，并返回一个字典输出。\n",
    "    - `run`：一个方便的方法，它以args/kwargs的形式接收输入，并将输出作为字符串或对象返回。这种方法只能用于一部分链，不能像`__call__`那样返回丰富的输出。\n",
    "    \"\"\"\n",
    "\n",
    "    # 调用链\n",
    "    def invoke(\n",
    "        self, input: Dict[str, Any], config: Optional[runnableConfig] = None\n",
    "    ) -> Dict[str, Any]:\n",
    "        \"\"\"传统调用方法。\"\"\"\n",
    "        return self(input, **(config or {}))\n",
    "\n",
    "    # 链的记忆，保存状态和变量\n",
    "    memory: Optional[BaseMemory] = None\n",
    "    \"\"\"可选的内存对象，默认为None。\n",
    "    内存是一个在每个链的开始和结束时被调用的类。在开始时，内存加载变量并在链中传递它们。在结束时，它保存任何返回的变量。\n",
    "    有许多不同类型的内存，请查看内存文档以获取完整的目录。\"\"\"\n",
    "\n",
    "    # 回调，可能用于链的某些操作或事件。\n",
    "    callbacks: Callbacks = Field(default=None, exclude=True)\n",
    "    \"\"\"可选的回调处理程序列表（或回调管理器）。默认为None。\n",
    "    在对链的调用的生命周期中，从on_chain_start开始，到on_chain_end或on_chain_error结束，都会调用回调处理程序。\n",
    "    每个自定义链可以选择调用额外的回调方法，详细信息请参见Callback文档。\"\"\"\n",
    "\n",
    "    # 是否详细输出模式\n",
    "    verbose: bool = Field(default_factory=_get_verbosity)\n",
    "    \"\"\"是否以详细模式运行。在详细模式下，一些中间日志将打印到控制台。默认值为`langchain.verbose`。\"\"\"\n",
    "\n",
    "    # 与链关联的标签\n",
    "    tags: Optional[List[str]] = None\n",
    "    \"\"\"与链关联的可选标签列表，默认为None。\n",
    "    这些标签将与对这个链的每次调用关联起来，并作为参数传递给在`callbacks`中定义的处理程序。\n",
    "    你可以使用这些来例如识别链的特定实例与其用例。\"\"\"\n",
    "\n",
    "    # 与链关联的元数据\n",
    "    metadata: Optional[Dict[str, Any]] = None\n",
    "    \"\"\"与链关联的可选元数据，默认为None。\n",
    "    这些元数据将与对这个链的每次调用关联起来，并作为参数传递给在`callbacks`中定义的处理程序。\n",
    "    你可以使用这些来例如识别链的特定实例与其用例。\"\"\"\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5d51fbb4-1d8e-4ec1-8c55-ec70247d4d64",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "c81a7df0-26c7-4eb8-92f1-cc54445cf507",
   "metadata": {},
   "source": [
    "## LLMChain\n",
    "\n",
    "LLMChain 是 LangChain 中最简单的链，作为其他复杂 Chains 和 Agents 的内部调用，被广泛应用。\n",
    "\n",
    "一个LLMChain由PromptTemplate和语言模型（LLM or Chat Model）组成。它使用直接传入（或 memory 提供）的 key-value 来规范化生成 Prompt Template（提示模板），并将生成的 prompt （格式化后的字符串）传递给大模型，并返回大模型输出。\n",
    "\n",
    "![](../images/llm_chain.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "757a67a6-c1aa-4dde-94ef-fb9865dc634c",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import OpenAI\n",
    "from langchain.prompts import PromptTemplate\n",
    "\n",
    "llm = OpenAI(model_name=\"gpt-3.5-turbo-instruct\", temperature=0.9, max_tokens=500)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "0b863511-ee01-43e8-8540-4e3f109a5a1a",
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt = PromptTemplate(\n",
    "    input_variables=[\"product\"],\n",
    "    template=\"给制造{product}的有限公司取10个好名字，并给出完整的公司名称\",\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "b877560c-cb66-41ad-b484-b2df2a60a00d",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\lenovo\\AppData\\Roaming\\Python\\Python310\\site-packages\\langchain_core\\_api\\deprecation.py:139: LangChainDeprecationWarning: The class `LLMChain` was deprecated in LangChain 0.1.17 and will be removed in 1.0. Use RunnableSequence, e.g., `prompt | llm` instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'product': '性能卓越的GPU', 'text': '\\n\\n1. 星火卓越科技有限公司\\n2. 利益达太空科技有限公司\\n3. 未来视界智能科技有限公司\\n4. 强力算力科技有限公司\\n5. 极速计算科技有限公司\\n6. 无限创想智能科技有限公司\\n7. 高能创新科技有限公司\\n8. 天马行空科技有限公司\\n9. 极致计算科技有限公司\\n10. 全能加速科技有限公司'}\n"
     ]
    }
   ],
   "source": [
    "from langchain.chains import LLMChain\n",
    "\n",
    "chain = LLMChain(llm=llm, prompt=prompt)\n",
    "print(chain.invoke({\n",
    "    'product': \"性能卓越的GPU\"\n",
    "    }))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "e8c7012e-e37e-4224-ba5f-e7132d16f684",
   "metadata": {},
   "outputs": [],
   "source": [
    "chain.verbose =True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "916ec42a-002b-4912-a599-a7be909e55ff",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.verbose"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "cfa71d7c-2859-47e1-9815-4be2ec9dbd74",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n",
      "Prompt after formatting:\n",
      "\u001b[32;1m\u001b[1;3m给制造性能卓越的GPU的有限公司取10个好名字，并给出完整的公司名称\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "{'product': '性能卓越的GPU', 'text': '\\n\\n\\n1. 锐芯科技有限公司（Sharpcore Technology Co., Ltd.）\\n2. 超晶科技有限公司（Supercrystal Technology Co., Ltd.）\\n3. 星云科技有限公司（Starcloud Technology Co., Ltd.）\\n4. 弦火科技有限公司（Chordfire Technology Co., Ltd.）\\n5. 极光声浪有限公司（Aurora Soundwave Co., Ltd.）\\n6. 炫影视界有限公司（Luminous Vision Co., Ltd.）\\n7. 梦幻芯片有限公司（Dreamchip Co., Ltd.）\\n8. 科创纵横有限公司（Innovate Horizon Co., Ltd.）\\n9. 虚拟巅峰有限公司（Virtual Summit Co., Ltd.）\\n10. 神威科技有限公司（Divineforce Technology Co., Ltd.）'}\n"
     ]
    }
   ],
   "source": [
    "print(chain.invoke({\n",
    "    'product': \"性能卓越的GPU\"\n",
    "    }))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ac5411e7-b8ec-4c31-b659-deb44af038df",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "99cbf75e-98f4-4c99-b8a7-9a48cc28c7bc",
   "metadata": {},
   "source": [
    "## Sequential Chain\n",
    "\n",
    "串联式调用语言模型（将一个调用的输出作为另一个调用的输入）。\n",
    "\n",
    "顺序链（Sequential Chain ）允许用户连接多个链并将它们组合成执行特定场景的流水线（Pipeline）。有两种类型的顺序链：\n",
    "\n",
    "- SimpleSequentialChain：最简单形式的顺序链，每个步骤都具有单一输入/输出，并且一个步骤的输出是下一个步骤的输入。\n",
    "- SequentialChain：更通用形式的顺序链，允许多个输入/输出。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e192c8c-49fc-4d04-8444-e6aa6bd7b725",
   "metadata": {},
   "source": [
    "### 使用 SimpleSequentialChain 实现戏剧摘要和评论（单输入/单输出）\n",
    "\n",
    "![](../images/simple_sequential_chain_0.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "a4d192a2-d563-4ab7-979f-640fa34f1914",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 这是一个 LLMChain，用于根据剧目的标题撰写简介。\n",
    "\n",
    "llm = OpenAI(temperature=0.7, max_tokens=1000)\n",
    "\n",
    "template = \"\"\"你是一位剧作家。根据戏剧的标题，你的任务是为该标题写一个简介。\n",
    "\n",
    "标题：{title}\n",
    "剧作家：以下是对上述戏剧的简介：\"\"\"\n",
    "\n",
    "prompt_template = PromptTemplate(input_variables=[\"title\"], template=template)\n",
    "synopsis_chain = LLMChain(llm=llm, prompt=prompt_template)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "3f7d429b-7ba7-4643-bd9f-fdb737ebf964",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 这是一个LLMChain，用于根据剧情简介撰写一篇戏剧评论。\n",
    "# llm = OpenAI(temperature=0.7, max_tokens=1000)\n",
    "template = \"\"\"你是《纽约时报》的戏剧评论家。根据剧情简介，你的工作是为该剧撰写一篇评论。\n",
    "\n",
    "剧情简介：\n",
    "{synopsis}\n",
    "\n",
    "以下是来自《纽约时报》戏剧评论家对上述剧目的评论：\"\"\"\n",
    "\n",
    "prompt_template = PromptTemplate(input_variables=[\"synopsis\"], template=template)\n",
    "review_chain = LLMChain(llm=llm, prompt=prompt_template)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5265129-5ccd-4e29-b221-0ec24eb84c2b",
   "metadata": {},
   "source": [
    "![](../images/simple_sequential_chain_1.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "de4d816e-16e1-4382-9064-6c03e5841ea2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 这是一个SimpleSequentialChain，按顺序运行这两个链\n",
    "from langchain.chains import SimpleSequentialChain\n",
    "\n",
    "overall_chain = SimpleSequentialChain(chains=[synopsis_chain, review_chain], verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "d503ac4f-e337-4436-86a1-7fd937efb06a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new SimpleSequentialChain chain...\u001b[0m\n",
      "\u001b[36;1m\u001b[1;3m\n",
      "\n",
      "《三体人不是无法战胜的》是一部关于宇宙中最强大的种族——三体人的故事。在这个宇宙中，三体人被认为是无法战胜的，因为他们拥有超越常人的科技和力量。然而，一位普通人类的出现改变了一切。他发现了三体人的弱点，并决心与他们抗争，为人类的自由而战。在与三体人的殊死搏斗中，他发现三体人并非不可战胜，而是因为他们的内心缺乏爱和同情。最终，普通人类的勇气和坚持，打破了三体人的统治，带来了和平与和谐的新时代。这部戏剧探讨了人类与外星种族的关系，以及爱和同情的力量对战争的影响。它将带领观众进入一个惊险刺激的宇宙冒险，同时也给人们带来思考和启发。\u001b[0m\n",
      "\u001b[33;1m\u001b[1;3m\n",
      "\n",
      "《三体人不是无法战胜的》是一部充满惊险和启发的戏剧作品。它通过一个普通人类与宇宙中最强大的种族——三体人的对抗，探讨了人类与外星种族的关系，以及爱和同情的力量对战争的影响。\n",
      "\n",
      "这部作品精彩地展现了三体人的科技与力量，以及他们对人类的统治。观众们将被带入一个充满刺激的宇宙冒险，与主角一起感受战斗的紧张与惊险。同时，剧中也深刻地探讨了三体人的弱点，以及人类如何通过勇气和坚持最终战胜他们。\n",
      "\n",
      "最令人感动的是，剧中展现了爱和同情的力量。主角发现，三体人之所以无法战胜，并不是因为他们的科技和力量，而是因为他们内心缺乏爱和同情。这也引发了人们对战争的思考，让观众们深刻地反思爱和同情对于和平与和谐的重要性。\n",
      "\n",
      "整部戏剧的制作也十分精彩，舞台设计、服装和道具都充分展现了宇宙的奇幻和未来的科技感。演员们的表演也令人印象深刻，他们生动地诠释了各个角色的复杂心理和情感。\n",
      "\n",
      "总的来说，《三体人不是无法战胜的》是一部令人难忘的戏剧作品。它不仅带给观众惊险的冒险和刺激的战斗，更重要的是，它让我们思考人类与外星种族的关系，以及爱和同情的力量对于和平与和谐的重要性。这部作品将在观众心中留下深刻的印象，值得一看。\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "review = overall_chain.invoke(\"三体人不是无法战胜的\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "ce4d75e1-8c57-4583-be7d-60a3488e35b5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new SimpleSequentialChain chain...\u001b[0m\n",
      "\u001b[36;1m\u001b[1;3m\n",
      "《星球大战第九季》是一部充满惊险刺激的科幻戏剧，它延续了《星球大战》系列的传奇故事。在这一季中，我们将见证新的冒险和挑战，因为银河系再次陷入战争的深渊。反抗军和第一秩序之间的战争仍在继续，但这一次，他们将面临着更加强大的敌人。新的角色和老朋友将再次出现，与我们的英雄们一起并肩作战。随着战争的进展，我们也将见证力量和黑暗的永恒对抗，以及对自由和正义的不懈追求。在这场史诗般的战役中，我们将看到勇气、牺牲和爱的力量，同时也会思考我们对未来的选择和命运。《星球大战第九季》将带给观众无与伦比的视觉盛宴和感人心弦的故事，让我们一起来见证这一壮丽的星际冒险！\u001b[0m\n",
      "\u001b[33;1m\u001b[1;3m\n",
      "\n",
      "《星球大战第九季》是一部不容错过的史诗般的科幻戏剧。该剧延续了《星球大战》系列的传奇故事，将观众带入一个充满惊险和刺激的银河系。在这一季中，我们将再次与我们熟悉的英雄们一起并肩作战，同时也会见证新的挑战和冒险。\n",
      "\n",
      "该剧不仅仅是一场视觉盛宴，更是一部让人思考的故事。通过力量和黑暗的永恒对抗，以及对自由和正义的追求，观众将被带入一个关于命运和选择的深刻思考。在这场史诗般的战役中，我们将见证勇气、牺牲和爱的力量，这些都是《星球大战》系列一直探讨的重要主题。\n",
      "\n",
      "该剧不仅有着令人惊叹的特效和精彩的动作场面，更重要的是它成功地将这些元素与深刻的人物关系和情感表达相结合。观众将会被带入一个充满希望和感动的星际冒险，与角色们一起经历他们的成长和转变。\n",
      "\n",
      "总的来说，《星球大战第九季》是一部充满情感和故事的出色作品，它将让观众沉浸在一个充满惊喜和挑战的星际世界。无论您是《星球大战》系列的忠实粉丝，还是对科幻题材感兴趣的观众，这部戏剧都会给您带来一场令人难忘的体验。不容错过！\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "review = overall_chain.invoke(\"星球大战第九季\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5fe32f1d-475d-4211-9b32-0c66dd8bff01",
   "metadata": {},
   "source": [
    "### 使用 SequentialChain 实现戏剧摘要和评论（多输入/多输出）\n",
    "\n",
    "![](../images/sequential_chain_0.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "2a04d84f-15c6-4a8d-a4db-200dfa405afa",
   "metadata": {},
   "outputs": [],
   "source": [
    "# # 这是一个 LLMChain，根据剧名和设定的时代来撰写剧情简介。\n",
    "llm = OpenAI(temperature=.7, max_tokens=1000)\n",
    "template = \"\"\"你是一位剧作家。根据戏剧的标题和设定的时代，你的任务是为该标题写一个简介。\n",
    "\n",
    "标题：{title}\n",
    "时代：{era}\n",
    "剧作家：以下是对上述戏剧的简介：\"\"\"\n",
    "\n",
    "prompt_template = PromptTemplate(input_variables=[\"title\", \"era\"], template=template)\n",
    "# output_key\n",
    "synopsis_chain = LLMChain(llm=llm, prompt=prompt_template, output_key=\"synopsis\", verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "250afe66-e014-4097-9798-f9ba812023fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 这是一个LLMChain，用于根据剧情简介撰写一篇戏剧评论。\n",
    "\n",
    "template = \"\"\"你是《纽约时报》的戏剧评论家。根据该剧的剧情简介，你需要撰写一篇关于该剧的评论。\n",
    "\n",
    "剧情简介：\n",
    "{synopsis}\n",
    "\n",
    "来自《纽约时报》戏剧评论家对上述剧目的评价：\"\"\"\n",
    "\n",
    "prompt_template = PromptTemplate(input_variables=[\"synopsis\"], template=template)\n",
    "review_chain = LLMChain(llm=llm, prompt=prompt_template, output_key=\"review\", verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "9eb46f6d-841b-4b87-9ed5-a5913ef9aec5",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.chains import SequentialChain\n",
    "\n",
    "m_overall_chain = SequentialChain(\n",
    "    chains=[synopsis_chain, review_chain],\n",
    "    input_variables=[\"era\", \"title\"],\n",
    "    # Here we return multiple variables\n",
    "    output_variables=[\"synopsis\", \"review\"],\n",
    "    verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "5a4a12ef-da2a-42ad-8044-fb71aedd3e2d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new SequentialChain chain...\u001b[0m\n",
      "\n",
      "\n",
      "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n",
      "Prompt after formatting:\n",
      "\u001b[32;1m\u001b[1;3m你是一位剧作家。根据戏剧的标题和设定的时代，你的任务是为该标题写一个简介。\n",
      "\n",
      "标题：三体人不是无法战胜的\n",
      "时代：二十一世纪的新中国\n",
      "剧作家：以下是对上述戏剧的简介：\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "\n",
      "\n",
      "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n",
      "Prompt after formatting:\n",
      "\u001b[32;1m\u001b[1;3m你是《纽约时报》的戏剧评论家。根据该剧的剧情简介，你需要撰写一篇关于该剧的评论。\n",
      "\n",
      "剧情简介：\n",
      "\n",
      "\n",
      "《三体人不是无法战胜的》讲述的是发生在二十一世纪新中国的故事。在这个时代，人类社会已经发展到了一个全新的境界，科技的进步让人类可以探索宇宙的奥秘。然而，就在这个时候，地球突然受到了外星种族三体人的入侵。\n",
      "\n",
      "三体人拥有强大的科技实力和高度发达的文明，他们的出现让地球陷入了前所未有的危机之中。在三体人的攻击下，人类社会陷入混乱，无数的生命被夺走，城市被摧毁，人们被迫逃离家园。\n",
      "\n",
      "然而，正当人类似乎无力抵抗的时候，一批勇敢的人们站了出来，他们来自不同的国家和阶层，但都有着同样的信念：三体人并不是无法战胜的。他们决定联合起来，利用自己的智慧和勇气，与三体人展开一场生死搏斗。\n",
      "\n",
      "在这场战争中，人类付出了巨大的牺牲，但也取得了重要的胜利。最终，人类成功击退了三体人的入侵，地球重新恢复了和平与安宁。\n",
      "\n",
      "《三体人不是无法战胜的》是一部关于人类勇气和团结的故事，它向我们展示了在面对未知和危机时，人类的坚强和不屈不挠。同时也提醒我们，科技的发展虽然能给人类带来前所未有的进步，但也需要我们谨慎使用，以免遭受意想不到的后果。\n",
      "\n",
      "来自《纽约时报》戏剧评论家对上述剧目的评价：\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'title': '三体人不是无法战胜的',\n",
       " 'era': '二十一世纪的新中国',\n",
       " 'synopsis': '\\n\\n《三体人不是无法战胜的》讲述的是发生在二十一世纪新中国的故事。在这个时代，人类社会已经发展到了一个全新的境界，科技的进步让人类可以探索宇宙的奥秘。然而，就在这个时候，地球突然受到了外星种族三体人的入侵。\\n\\n三体人拥有强大的科技实力和高度发达的文明，他们的出现让地球陷入了前所未有的危机之中。在三体人的攻击下，人类社会陷入混乱，无数的生命被夺走，城市被摧毁，人们被迫逃离家园。\\n\\n然而，正当人类似乎无力抵抗的时候，一批勇敢的人们站了出来，他们来自不同的国家和阶层，但都有着同样的信念：三体人并不是无法战胜的。他们决定联合起来，利用自己的智慧和勇气，与三体人展开一场生死搏斗。\\n\\n在这场战争中，人类付出了巨大的牺牲，但也取得了重要的胜利。最终，人类成功击退了三体人的入侵，地球重新恢复了和平与安宁。\\n\\n《三体人不是无法战胜的》是一部关于人类勇气和团结的故事，它向我们展示了在面对未知和危机时，人类的坚强和不屈不挠。同时也提醒我们，科技的发展虽然能给人类带来前所未有的进步，但也需要我们谨慎使用，以免遭受意想不到的后果。',\n",
       " 'review': '\\n\\n《三体人不是无法战胜的》是一部令人惊叹的现代科幻戏剧。它不仅展现了人类社会的科技进步和探索宇宙的梦想，更重要的是通过讲述与外星种族三体人的战争，向我们传递出关于人类团结和勇气的深刻信息。\\n\\n该剧的故事情节紧凑、扣人心弦，将观众带入一个充满未知和危机的世界。在三体人的入侵下，人类社会陷入混乱，但也正是在这种极端的情况下，人们的团结和勇气才得以展现。从不同国家和阶层的人们联合起来，到最终成功击退入侵者，这部剧给人们带来了希望和勇气的力量。\\n\\n此外，该剧还提醒我们科技发展的另一面。虽然科技能为人类带来前所未有的进步，但也需要谨慎使用，以免遭受意想不到的后果。这种深刻的思考让观众在欣赏剧目的同时，也能反思自己对科技的使用态度。\\n\\n总的来说，《三体人不是无法战胜的》是一部令人兴奋、思考和感动的现代戏剧。它将人类的勇气和团结精神传递给观众，同时也提醒我们在面对未知和挑战时，需要保持谨慎和勇气。这部剧绝对值得一看。'}"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "m_overall_chain.invoke({\"title\":\"三体人不是无法战胜的\", \"era\": \"二十一世纪的新中国\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8c20cf4e-25b4-453d-9f7a-84138ca25cf8",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5ef1db6e-3da4-4f9b-9707-0f30aa293dd7",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "8b6836f0-213d-4cac-abc9-3617831be3db",
   "metadata": {},
   "source": [
    "### Homework\n",
    "\n",
    "#### 使用 OutputParser 优化 overall_chain 输出格式，区分 synopsis_chain 和 review_chain 的结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1c7edb0a-675d-40c0-9f5d-d58f0170ce72",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
